/*
 * pio_it_rtos.c
 *
 *  Created on: 08.06.2010
 *      Author: Phil
 */

/// Disable traces for this file
#undef TRACE_LEVEL
#define TRACE_LEVEL 0

#include <board.h>
#include <pio/pio.h>
#include <aic/aic.h>
#include <utility/assert.h>
#include <utility/trace.h>
#include "irqproxy.h"

#define MAX_INTERRUPT_SOURCES       7

//------------------------------------------------------------------------------
//         Local types
//------------------------------------------------------------------------------
typedef struct {

    /// Pointer to the source pin instance.
    const Pin *pPin;

    /// Interrupt handler.
    void (*handler)(const Pin *);

} InterruptSource;

//------------------------------------------------------------------------------
//         Local variables
//------------------------------------------------------------------------------

/// List of interrupt sources.
static InterruptSource pSources[MAX_INTERRUPT_SOURCES];

/// Number of currently defined interrupt sources.
static unsigned int numSources = 0;

//------------------------------------------------------------------------------
//         Local functions
//------------------------------------------------------------------------------

static void PioInterruptHandler(unsigned int id, AT91S_PIO *pPio)
{
    unsigned int status;
    unsigned int i;

    // Read PIO controller status
    status = pPio->PIO_ISR;
    status &= pPio->PIO_IMR;

    // Check pending events
    if (status != 0) {

        TRACE_DEBUG("PIO interrupt on PIO controller #%d\n\r", id);

        // Find triggering source
        i = 0;
        while (status != 0) {

            // There cannot be an unconfigured source enabled.
            SANITY_CHECK(i < numSources);

            // Source is configured on the same controller
            if (pSources[i].pPin->id == id) {

                // Source has PIOs whose statuses have changed
                if ((status & pSources[i].pPin->mask) != 0) {

                    TRACE_DEBUG("Interrupt source #%d triggered\n\r", i);

                    pSources[i].handler(pSources[i].pPin);
                    status &= ~(pSources[i].pPin->mask);
                }
            }
            i++;
        }
    }
}

void PIO_InterruptHandler(void)
{
#if defined(AT91C_ID_PIOA)
    // Treat PIOA interrupts
    PioInterruptHandler(AT91C_ID_PIOA, AT91C_BASE_PIOA);
#endif

#if defined(AT91C_ID_PIOB)
    // Treat PIOB interrupts
    PioInterruptHandler(AT91C_ID_PIOB, AT91C_BASE_PIOB);
#endif

#if defined(AT91C_ID_PIOC)
    // Treat PIOC interrupts
    PioInterruptHandler(AT91C_ID_PIOC, AT91C_BASE_PIOC);
#endif

#if defined(AT91C_ID_PIOD)
    // Treat PIOD interrupts
    PioInterruptHandler(AT91C_ID_PIOD, AT91C_BASE_PIOD);
#endif

#if defined(AT91C_ID_PIOE)
    // Treat PIOE interrupts
    PioInterruptHandler(AT91C_ID_PIOE, AT91C_BASE_PIOE);
#endif

#if defined(AT91C_ID_PIOABCD)
    // Treat PIOABCD interrupts
    #if !defined(AT91C_ID_PIOA)
        PioInterruptHandler(AT91C_ID_PIOABCD, AT91C_BASE_PIOA);
    #endif
    #if !defined(AT91C_ID_PIOB)
        PioInterruptHandler(AT91C_ID_PIOABCD, AT91C_BASE_PIOB);
    #endif
    #if !defined(AT91C_ID_PIOC)
        PioInterruptHandler(AT91C_ID_PIOABCD, AT91C_BASE_PIOC);
    #endif
    #if !defined(AT91C_ID_PIOD)
        PioInterruptHandler(AT91C_ID_PIOABCD, AT91C_BASE_PIOD);
    #endif
#endif

#if defined(AT91C_ID_PIOABCDE)
    // Treat PIOABCDE interrupts
    #if !defined(AT91C_ID_PIOA)
        PioInterruptHandler(AT91C_ID_PIOABCDE, AT91C_BASE_PIOA);
    #endif
    #if !defined(AT91C_ID_PIOB)
        PioInterruptHandler(AT91C_ID_PIOABCDE, AT91C_BASE_PIOB);
    #endif
    #if !defined(AT91C_ID_PIOC)
        PioInterruptHandler(AT91C_ID_PIOABCDE, AT91C_BASE_PIOC);
    #endif
    #if !defined(AT91C_ID_PIOD)
        PioInterruptHandler(AT91C_ID_PIOABCDE, AT91C_BASE_PIOD);
    #endif
    #if !defined(AT91C_ID_PIOE)
        PioInterruptHandler(AT91C_ID_PIOABCDE, AT91C_BASE_PIOE);
    #endif
#endif

#if defined(AT91C_ID_PIOCDE)
    // Treat PIOCDE interrupts
    #if !defined(AT91C_ID_PIOC)
        PioInterruptHandler(AT91C_ID_PIOCDE, AT91C_BASE_PIOC);
    #endif
    #if !defined(AT91C_ID_PIOD)
        PioInterruptHandler(AT91C_ID_PIOCDE, AT91C_BASE_PIOD);
    #endif
    #if !defined(AT91C_ID_PIOE)
        PioInterruptHandler(AT91C_ID_PIOCDE, AT91C_BASE_PIOE);
    #endif
#endif
}

void PIO_InitializeInterrupts(unsigned int priority)
{
	TRACE_DEBUG("PIO_Initialize()\n\r");

	SANITY_CHECK((priority & ~AT91C_AIC_PRIOR) == 0);

	// Reset sources
	numSources = 0;

#ifdef AT91C_ID_PIOA
	// Configure PIO interrupt sources
	TRACE_DEBUG("PIO_Initialize: Configuring PIOA\n\r");
	AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_PIOA;
	AT91C_BASE_PIOA->PIO_ISR;
	AT91C_BASE_PIOA->PIO_IDR = 0xFFFFFFFF;
	AIC_ConfigureIT(AT91C_ID_PIOA, priority, vIRQProxy);
	AIC_EnableIT(AT91C_ID_PIOA);
#endif

#ifdef AT91C_ID_PIOB
	TRACE_DEBUG("PIO_Initialize: Configuring PIOB\n\r");
	AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_PIOB;
	AT91C_BASE_PIOB->PIO_ISR;
	AT91C_BASE_PIOB->PIO_IDR = 0xFFFFFFFF;
	AIC_ConfigureIT(AT91C_ID_PIOB, priority, vIRQProxy);
	AIC_EnableIT(AT91C_ID_PIOB);
#endif

#ifdef AT91C_ID_PIOC
	TRACE_DEBUG("PIO_Initialize: Configuring PIOC\n\r");
	AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_PIOC;
	AT91C_BASE_PIOC->PIO_ISR;
	AT91C_BASE_PIOC->PIO_IDR = 0xFFFFFFFF;
	AIC_ConfigureIT(AT91C_ID_PIOC, priority, vIRQProxy);
	AIC_EnableIT(AT91C_ID_PIOC);
#endif

#ifdef AT91C_ID_PIOD
	TRACE_DEBUG("PIO_Initialize: Configuring PIOD\n\r");
	AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_PIOD;
	AT91C_BASE_PIOC->PIO_ISR;
	AT91C_BASE_PIOC->PIO_IDR = 0xFFFFFFFF;
	AIC_ConfigureIT(AT91C_ID_PIOD, priority, vIRQProxy);
	AIC_EnableIT(AT91C_ID_PIOD);
#endif

#ifdef AT91C_ID_PIOE
	TRACE_DEBUG("PIO_Initialize: Configuring PIOE\n\r");
	AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_PIOE;
	AT91C_BASE_PIOC->PIO_ISR;
	AT91C_BASE_PIOC->PIO_IDR = 0xFFFFFFFF;
	AIC_ConfigureIT(AT91C_ID_PIOE, priority, vIRQProxy);
	AIC_EnableIT(AT91C_ID_PIOE);
#endif

#if defined(AT91C_ID_PIOABCD)
	// Treat PIOABCD interrupts
	#if !defined(AT91C_ID_PIOA) \
	 && !defined(AT91C_ID_PIOB) \
	 && !defined(AT91C_ID_PIOC) \
	 && !defined(AT91C_ID_PIOD)

		TRACE_DEBUG("PIO_Initialize: Configuring PIOABCD\n\r");
		AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_PIOABCD;
		AT91C_BASE_PIOA->PIO_ISR;
		AT91C_BASE_PIOA->PIO_IDR = 0xFFFFFFFF;
		AIC_ConfigureIT(AT91C_ID_PIOABCD, priority, vIRQProxy);
		AIC_EnableIT(AT91C_ID_PIOABCD);
	#endif
#endif

#if defined(AT91C_ID_PIOABCDE)
	// Treat PIOABCDE interrupts
	#if !defined(AT91C_ID_PIOA) \
	 && !defined(AT91C_ID_PIOB) \
	 && !defined(AT91C_ID_PIOC) \
	 && !defined(AT91C_ID_PIOD) \
	 && !defined(AT91C_ID_PIOE)

		TRACE_DEBUG("PIO_Initialize: Configuring PIOABCDE\n\r");
		AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_PIOABCDE;
		AT91C_BASE_PIOA->PIO_ISR;
		AT91C_BASE_PIOA->PIO_IDR = 0xFFFFFFFF;
		AIC_ConfigureIT(AT91C_ID_PIOABCDE, priority, vIRQProxy);
		AIC_EnableIT(AT91C_ID_PIOABCDE);
	#endif
#endif

#if defined(AT91C_ID_PIOCDE)
	// Treat PIOCDE interrupts
	#if !defined(AT91C_ID_PIOC) \
	 && !defined(AT91C_ID_PIOD) \
	 && !defined(AT91C_ID_PIOE)

		TRACE_DEBUG("PIO_Initialize: Configuring PIOC\n\r");
		AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_PIOCDE;
		AT91C_BASE_PIOC->PIO_ISR;
		AT91C_BASE_PIOC->PIO_IDR = 0xFFFFFFFF;
		AIC_ConfigureIT(AT91C_ID_PIOCDE, priority, vIRQProxy);
		AIC_EnableIT(AT91C_ID_PIOCDE);
	#endif
#endif
}

void PIO_ConfigureIt(const Pin *pPin, void (*handler)(const Pin *))
{
    InterruptSource *pSource;

    TRACE_DEBUG("PIO_ConfigureIt()\n\r");

    SANITY_CHECK(pPin);
    ASSERT(numSources < MAX_INTERRUPT_SOURCES,
           "-F- PIO_ConfigureIt: Increase MAX_INTERRUPT_SOURCES\n\r");

    // Define new source
    TRACE_DEBUG("PIO_ConfigureIt: Defining new source #%d.\n\r",  numSources);

    pSource = &(pSources[numSources]);
    pSource->pPin = pPin;
    pSource->handler = handler;
    numSources++;
}

void PIO_EnableIt(const Pin *pPin)
{
    TRACE_DEBUG("PIO_EnableIt()\n\r");

    SANITY_CHECK(pPin);

#ifndef NOASSERT
    unsigned int i = 0;
    unsigned char found = 0;
    while ((i < numSources) && !found) {

        if (pSources[i].pPin == pPin) {

            found = 1;
        }
        i++;
    }
    ASSERT(found, "-F- PIO_EnableIt: Interrupt source has not been configured\n\r");
#endif

    pPin->pio->PIO_ISR;
    pPin->pio->PIO_IER = pPin->mask;
}

void PIO_DisableIt(const Pin *pPin)
{
    SANITY_CHECK(pPin);

    TRACE_DEBUG("PIO_DisableIt()\n\r");

    pPin->pio->PIO_IDR = pPin->mask;
}
